﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMeterParamFrame;
using SmartMeterParamFrame.Connection;

namespace CL2018_1
{

    /// <summary>
    /// 2018_1 类型驱动程序
    /// </summary>
    class CL2018_1Device : SmartMeterParamFrame.IDevice
    {
        /// <summary>
        /// 外部提供的宿主UI
        /// </summary>
        private IApplication hostApplication = null;

        /// <summary>
        /// 配置界面UI
        /// </summary>
        UI.UI_Config configUI = null;

        /// <summary>
        /// 配置数据
        /// </summary>
        Model.Config configData = null;

        /// <summary>
        /// rs 485 总线数量
        /// </summary>
        public int BusCount
        {
            get { return this.configData.Rs485Count; }
        }

        /// <summary>
        /// 表位数
        /// </summary>
        public int Bws
        {
            get { return this.configData.Bws; }
        }

        /// <summary>
        /// 是否是 188H
        /// </summary>
        public bool IsChannnelConst
        {
            get { return this.configData.Is188H; }
        }

        /// <summary>
        /// 要开启的线程数量
        /// </summary>
        public int ThreadCount
        {
            get { return this.configData.Rs485Count; }
        }

        /// <summary>
        /// 设置 外部的 UI宿主
        /// </summary>
        public SmartMeterParamFrame.IApplication UISet
        {
            set { this.hostApplication = value; }
        }

        UDPClient UdpSock_303;

        /// <summary>
        /// Dictionary&lt;端口号, Sock.UDPClient&gt;
        /// </summary>
        Dictionary<int, UDPClient> UdpSock_188 = new Dictionary<int, UDPClient>();

        /// <summary>
        /// Dictionary&lt;端口号, Sock.UDPClient&gt;
        /// </summary>
        Dictionary<int, UDPClient> UdpSock_485 = new Dictionary<int, UDPClient>();

        public CL2018_1Device()
        {
            this.configData = new Model.Config();
            InitNetwork();
        }

        /// <summary>
        /// 初始化网络环境
        /// </summary>
        private void InitNetwork()
        {
            if (this.UdpSock_303 != null)
                this.UdpSock_303.Close();
            this.UdpSock_303 = new UDPClient();

            foreach (UDPClient client in this.UdpSock_188.Values)
            {
                client.Close();
            }
            this.UdpSock_188.Clear();

            foreach (UDPClient client in this.UdpSock_485.Values)
            {
                client.Close();
            }
            this.UdpSock_485.Clear();
        }

        /// <summary>
        /// 显示配置界面
        /// </summary>
        public void ShowUI()
        {
            if (this.hostApplication == null)
                return;

            configUI = new UI.UI_Config(this.configData);
            ((System.Windows.Controls.TabItem)(this.hostApplication.ControlPanel)).Content = configUI;

            this.hostApplication.Save += new EventHandler(hostApplication_Save);
        }

        /// <summary>
        /// 保存配置
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void hostApplication_Save(object sender, EventArgs e)
        {
            try
            {
                this.hostApplication.IsSaveOK = this.configUI.Save();
                this.InitNetwork();
            }
            catch (Exception ex)
            {
                System.Windows.MessageBox.Show("保存数据失败:" + ex.Message);
                this.hostApplication.IsSaveOK = false;
            }
        }

        /// <summary>
        /// 联机命令
        /// </summary>
        /// <param name="ErrString"></param>
        /// <returns></returns>
        public bool DeviceLink(out string ErrString)
        {
            ErrString = string.Empty;
            byte[] byCmd_303 = CreateCLT11Command(0x20, new byte[] { (byte)'R' }); //源联机
            SendDataTo303(byCmd_303, true);
            return true;
        }

        /// <summary>
        /// 脱机命令
        /// </summary>
        /// <param name="ErrString"></param>
        /// <returns></returns>
        public bool DeviceOffLink(out string ErrString)
        {
            ErrString = string.Empty;
            byte[] byCmd_303 = CreateCLT11Command(0x20, new byte[] { 0x4F });
            SendDataTo303(byCmd_303, true);
            return true;
        }

        /// <summary>
        /// 生源命令
        /// </summary>
        /// <param name="U"></param>
        /// <param name="ErrString"></param>
        /// <returns></returns>
        public bool PowerOn(float U, out string ErrString)
        {
            ErrString = string.Empty;

            try
            {
                if (powerOn0x35(U, 0) == false)
                {
                    return powerOn0x30(U, 0);
                }
                return true;
            }
            catch
            {
                ErrString = "控制源输出失败";
            }
            return false;

        }

        /// <summary>
        /// 35指令控制源
        /// </summary>
        /// <param name="U"></param>
        /// <param name="I"></param>
        /// <returns></returns>
        private bool powerOn0x35(float U, float I)
        {
            //处理测量方式
            byte myClfs = GetClfs(U);

            myClfs &= 127;//去掉对标标志
            myClfs &= 191;//缓降
            myClfs &= 223;


            List<byte> LstCmdData = new List<byte>();
            LstCmdData.Add(0x35);
            LstCmdData.Add(myClfs);//测量方式
            LstCmdData.Add(0x10); //波段,只让加电流开关有效
            LstCmdData.Add(63);//相位开关 即ABC电压ABC电流，哪相输出哪相关闭

            PutUInt(LstCmdData, 0xFFFFFFFF);

            LstCmdData.Add(0xFF);
            LstCmdData.Add(0xFF);

            LstCmdData.Add(0x00);

            LstCmdData.Add(GetUScale(U));
            LstCmdData.Add(GetIScale(I));

            PutUInt(LstCmdData, (uint)(U * 65536));
            PutUInt(LstCmdData, (uint)I * 65536 * 100);
            PutUInt(LstCmdData, (uint)0);
            PutUInt(LstCmdData, (uint)(50 * 65536));
            LstCmdData.Add(0);
            LstCmdData.Add(100);
            PutUShort(LstCmdData, 0);

            byte[] byCmd_303 = CreateCLT11Command(0x20, LstCmdData.ToArray());
            bool ret = SendDataTo303(byCmd_303, true);
            return true;
        }


        /// <summary>
        /// 30指令控源
        /// </summary>
        /// <param name="U"></param>
        /// <param name="I"></param>
        /// <returns></returns>
        private bool powerOn0x30(float U, float I)
        {
            byte xwkz = 63;
            ByteBuffer buf = new ByteBuffer();
            //byte bytTmp = 0;
            byte clfs = GetClfs(U);
            switch (clfs)
            {
                case 7:
                    xwkz &= 9;
                    break;
                case 2:
                case 3:
                case 5:
                case 6:
                    xwkz &= 45;
                    break;
            }

            buf.Put(0x30);
            buf.Put(xwkz);                  //相位控制
            buf.Put(0);                     //谐波开关
            buf.Put(get10bitData(50F));    //频率
            //UA
            buf.Put(GetUScale(U));       //
            buf.Put(get10bitData(U));    //
            buf.Put(get10bitData(0)); //
            //Ub
            buf.Put(GetUScale(U));       //
            buf.Put(get10bitData(U));    //
            buf.Put(get10bitData(120)); //

            //Uc
            buf.Put(GetUScale(U));       //
            buf.Put(get10bitData(U));    //
            buf.Put(get10bitData(240)); //

            //Ia
            buf.Put(GetUScale(0));       //
            buf.Put(get10bitData(0));    //
            buf.Put(get10bitData(0)); //
            //Ib
            buf.Put(GetUScale(0));       //
            buf.Put(get10bitData(0));    //
            buf.Put(get10bitData(120)); //
            //Ic
            buf.Put(GetUScale(0));       //
            buf.Put(get10bitData(0));    //
            buf.Put(get10bitData(240)); //

            byte[] byCmd_303 = CreateCLT11Command(0x20, buf.ToByteArray());
            return SendDataTo303(byCmd_303, true);
        }

        /// <summary>
        /// 关源命令
        /// </summary>
        /// <param name="ErrString"></param>
        /// <returns></returns>
        public bool PowerOff(out string ErrString)
        {
            ErrString = string.Empty;
            byte[] byCmd_303 = CreateCLT11Command(0x20, new byte[] { 0x45 });
            SendDataTo303(byCmd_303, true);
            return true;
        }

        /// <summary>
        /// 设置误差版本打开与关闭
        /// </summary>
        /// <param name="Bwh"></param>
        /// <param name="IsOpen"></param>
        public void SetWcb(int Bwh, bool IsOpen)
        {
            byte Cmd = (byte)(IsOpen ? 0x51 : 0x52);
            byte Data = (byte)Bwh;
            byte[] byCmdData = new byte[] { Cmd, Data };
            byte[] byCmd_188 = CreateCLT11Command(0x80, 0x10, byCmdData);//CLT11指令
            SendDataTo188(Bwh, byCmd_188, false);
        }

        /// <summary>
        /// 设置误差版打开与关闭
        /// </summary>
        /// <param name="WcbType"></param>
        /// <param name="IsOpen"></param>
        public void SetWcb(byte WcbType, bool IsOpen)
        {
            int bwh = (int)WcbType;
            if (WcbType == 0xFF) //广播所有误差板
            {
                if (this.configData.Port_188.Length == 1)
                {
                    SetWcb(bwh, IsOpen);
                }
                else
                {
                    for (int i = 1; i <= this.configData.Bws; i++)
                    {
                        SetWcb(i, IsOpen);
                    }
                }
            }
            else
            {
                SetWcb(bwh, IsOpen);
            }
        }

        /// <summary>
        /// 发送数据到电能表
        /// </summary>
        /// <param name="bytData"></param>
        /// <param name="IsReturn"></param>
        /// <param name="Bwh"></param>
        /// <returns></returns>
        public bool SendData(ref byte[] bytData, bool IsReturn, int Bwh)
        {
            byte[] byRecv = new byte[] { };
            bool blnReturn = SendDataToRs485(Bwh, bytData, ref byRecv, IsReturn);
            bytData = byRecv;
            return blnReturn;
        }


        /// <summary>
        /// 发送命令到303
        /// </summary>
        /// <param name="bySend"></param>
        /// <param name="blnNeedReturn"></param>
        /// <returns></returns>
        private bool SendDataTo303(byte[] bySend, bool blnNeedReturn)
        {
            byte[] byRecv = new byte[] { };
            bool blnRetrun = false;

            if (false == UdpSock_303.IsInit)
            {
                int port = 20000 + (this.configData.Power303Port - 1) * 2;
                UdpSock_303.Init("9600,N,8,1", "193.168.18.1", port, port + 1, 10003);
            }

            blnRetrun = UdpSock_303.SendData(ref bySend, blnNeedReturn);
            byRecv = bySend;

            return (false == blnNeedReturn) || (byRecv.Length == 6 && byRecv[4] == 0x4b);
        }

        /// <summary>
        /// 发送指令到188
        /// </summary>
        /// <param name="bwh"></param>
        /// <param name="bySend"></param>
        /// <param name="blnNeedReturn"></param>
        /// <returns></returns>
        private bool SendDataTo188(int bwh, byte[] bySend, bool blnNeedReturn)
        {
            return SendDataTo188ByPortId(this.configData.Get188Port(bwh), bySend, blnNeedReturn);
        }

        private bool SendDataTo188ByPortId(int portid, byte[] bySend, bool blnNeedReturn)
        {
            byte[] byRecv = new byte[] { };
            bool blnRetrun = false;

            if (false == UdpSock_188.ContainsKey(portid))
            {
                UdpSock_188.Add(portid, new UDPClient());
                UdpSock_188[portid].Init("9600,N,8,1", "193.168.18.1", portid, portid + 1, 10003);
            }
            if (!UdpSock_188[portid].IsInit) return false;
            blnRetrun = UdpSock_188[portid].SendData(ref bySend, blnNeedReturn);
            byRecv = bySend;

            return (false == blnNeedReturn) || (byRecv.Length == 6 && byRecv[4] == 0x30);
        }

        /// <summary>
        /// 发送RS485数据
        /// </summary>
        /// <param name="bwh"></param>
        /// <param name="bySend"></param>
        /// <param name="byRecv"></param>
        /// <param name="blnNeedReturn"></param>
        /// <returns></returns>
        private bool SendDataToRs485(int bwh, byte[] bySend, ref byte[] byRecv, bool blnNeedReturn)
        {
            if (false == this.UdpSock_485.ContainsKey(bwh))
            {
                this.UdpSock_485.Add(bwh, new UDPClient());
                UdpSock_485[bwh].Init("2400,e,8,1", "193.168.18.1", this.configData.Get485Port(bwh), this.configData.Get485Port(bwh) + 1, 10003);
            }
            if (!UdpSock_485[bwh].IsInit) return false;

            bool blnReturn = UdpSock_485[bwh].SendData(ref bySend, blnNeedReturn);
            byRecv = bySend;
            return blnReturn;
        }


        /// <summary>
        /// 生成CLT1.1命令
        /// </summary>
        /// <param name="myId"></param>
        /// <param name="toId"></param>
        /// <param name="cmdData"></param>
        /// <returns></returns>
        private byte[] CreateCLT11Command(byte myId, byte toId, byte[] cmdData)
        {
            List<byte> LstCmd = new List<byte>();
            LstCmd.Add((byte)0x81);
            LstCmd.Add(toId);
            LstCmd.Add(myId);
            LstCmd.Add((byte)(4 + cmdData.Length + 1));//包头+包 体+校验码
            LstCmd.AddRange(cmdData);

            byte chkSum = 0;
            for (int i = 1; i < LstCmd.Count; i++)
            {
                chkSum ^= LstCmd[i];
            }
            LstCmd.Add(chkSum);

            return LstCmd.ToArray();
        }

        /// <summary>
        /// 老版本的CLT11指令
        /// </summary>
        /// <param name="toId">受信节点ID</param>
        /// <param name="cmdData"></param>
        /// <returns></returns>
        private byte[] CreateCLT11Command(byte toId, byte[] cmdData)
        {
            List<byte> LstCmd = new List<byte>();
            LstCmd.Add((byte)0x81);
            LstCmd.Add(toId);
            PutUShort(LstCmd, (ushort)(4 + cmdData.Length + 1));
            LstCmd.AddRange(cmdData);
            byte chkSum = 0;
            for (int i = 1; i < LstCmd.Count; i++)
            {
                chkSum ^= LstCmd[i];
            }
            LstCmd.Add(chkSum);

            return LstCmd.ToArray();
        }

        /// <summary>
        /// 计算测量方式
        /// </summary>
        /// <param name="u"></param>
        /// <returns></returns>
        private byte GetClfs(float u)
        {
            byte myClfs = 0;
            if (this.configData.TaiType == Model.TaiType.单相台)
            {
                myClfs = 7;// 单相
            }
            else
            {
                if (u == 220)
                {
                    myClfs = 0; //三相四线
                }
                else
                {
                    myClfs = 2;//三相三线
                }
            }
            return myClfs;
        }


        /// <summary>
        /// 将数字转换成10字节数据
        /// </summary>
        /// <param name="Data"></param>
        /// <returns></returns>
        protected byte[] get10bitData(Single Data)
        {
            string sData = Convert.ToString(Data);
            if (sData.IndexOf('.') <= 0) sData += ".";
            sData += "0000000000";
            sData = sData.Substring(0, 9);
            byte[] bPara = ASCIIEncoding.ASCII.GetBytes(sData);
            Array.Resize(ref bPara, bPara.Length + 1);
            bPara[9] = 48;          //Convert.ToByte((sng_Value - Math.Floor(sng_Value)) == 0 ? 46 : 48);
            return bPara;
        }

        private byte GetIScale(Single sngI)
        {
            if (sngI <= 0.12) return 80;            //"50";
            else if (sngI <= 0.3) return 81;        //"51";
            else if (sngI <= 0.6) return 82;        // "52";
            else if (sngI <= 0.12) return 83;       // "53";
            else if (sngI <= 0.3) return 84;        //"54";
            else if (sngI <= 0.6) return 85;        //"55";
            else if (sngI <= 1.2) return 86;        //"56";
            else if (sngI <= 3) return 87;          // "57";
            else if (sngI <= 6) return 88;          //"58";
            else if (sngI <= 12) return 89;         //"59";
            else if (sngI <= 30) return 90;         //"5a";
            else if (sngI <= 60) return 91;         //"5b";
            else if (sngI <= 120) return 92;        // "5c";
            else return 92;// "5c";
        }

        private byte GetUScale(Single sngU)
        {
            if (sngU <= 57 * 1.2) return 64;//"40";
            else if (sngU <= 120) return 65;// "41";
            else if (sngU <= 264) return 66;// "42";
            else if (sngU <= 480) return 67;//"43";
            else if (sngU <= 900) return 68;//"44";
            else return 66;// "42";
        }

        /// <summary>
        /// 压入一个UInt,小端结尾
        /// </summary>
        /// <param name="LstBuffer"></param>
        /// <param name="value"></param>
        private void PutUInt(List<byte> LstBuffer, uint value)
        {
            LstBuffer.Add((byte)(value >> 0x18));
            LstBuffer.Add((byte)(value >> 0x10));
            LstBuffer.Add((byte)(value >> 8));
            LstBuffer.Add((byte)value);
        }

        /// <summary>
        /// 压入一个UShort,小端结尾
        /// </summary>
        /// <param name="LstBuffer"></param>
        /// <param name="value"></param>
        private void PutUShort(List<byte> LstBuffer, ushort value)
        {
            LstBuffer.Add((byte)(value >> 8));
            LstBuffer.Add((byte)value);
        }
    }
}
